<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Redis;
use App\Models\TestRedisMysql;
use Illuminate\Support\Facades\DB;

class RedisAppController extends Controller
{
    
    //
    public function index(){
         
    }
/*-----------------------------redis幂等性和分布式锁 start-----------------------------*/
    public function idempotentLock(){
        return view('test/idempotent_lock');
    }
    
    public function idempotentLockApi(Request $request){
        $nikoname = $request->input('nikoname');
        $age = $request->input('age');
        $sexy = $request->input('sexy');
        $hobby = $request->input('hobby');
        $pet = $request->input('pet');
        $created_at = date('Y-m-d H:i:s');
        $updated_at = date('Y-m-d H:i:s');      
        
        $data = [
            'nikoname' => $nikoname,
            'age' => $age,
            'sexy' => $sexy,
            'hobby' => $hobby,
            'pet' => $pet,
            'created_at' => $created_at,
            'updated_at' => $updated_at
        ];

        //使用redis的锁机制防止重复提交
        $lockKey = 'idempotent_lock_' . $nikoname; // 根据唯一标识生成锁的键
        $lockExpire = 10; // 锁的过期时间，单位为秒
        $lockValue = '9527';//锁的值，一般为request_id
        
        /*
        $redis = app('redis');
        if(!$redis){
             throw new \Exception('redis服务异常');
        }
         $result = $redis->setnx($lockKey, 1);
         //加过期时间，非原子性
        if ($result === 1 && $lockExpire) {
            $redis->expire($lockKey, $lockExpire);
        }
        */
        
        if (Redis::exists($lockKey)) {
            // 锁已存在，说明已经有其他进程在操作，返回错误信息
            return json_encode(['code' => 666, 'msg' => '请勿重复提交']);
        }
        // 尝试获取权限，设置锁；成功返回1，失败返0
        $lock = Redis::set($lockKey, $lockValue, 'EX', $lockExpire,'NX');
        //$lock = $this->onLock($lockKey,$lockValue,$lockExpire);
        if ($lock) {
            // 获取锁成功，执行插入操作
            //$res = DB::table('test_redis_mysql')->insert($data);
            $res = 1;
            if($res){

                // 释放锁 由于是为了防止重复提交，需等10秒锁失效所以不必手动删除，
                //等系统十秒后锁自动失效
                /*
                $val = Redis::get($lockKey);
                //防止误删
                if($val == $lockValue){
                    Redis::del($lockKey);
                }
                */     
                return json_encode(['code' => 200, 'msg' => '添加成功']);
            }else   {
                
                return json_encode(['code' => 500, 'msg' => '添加失败']);
            }
        } else {
            // 获取锁失败，说明已经有其他进程在操作，返回错误信息
            return json_encode(['code' => 666, 'msg' => '获取锁失败']);
        }
        
    }

    
    //lua脚本式的分布式锁
    public function idempotentLockLua(Request $request){
        $data = [
            'nikoname' => $request->input('nikoname'),
            'age' => $request->input('age'),
            'sexy' => $request->input('sexy'),
            'hobby' => $request->input('hobby'),
            'pet' => $request->input('pet'),
            'created_at' => date('Y-m-d H:i:s'),
            'updated_at' => date('Y-m-d H:i:s')
        ];

        //使用redis的锁机制防止重复提交
        $lockKey = 'idempotent_lock_' . $data['nikoname']; // 根据唯一标识生成锁的键
        $lockExpire = 10; // 锁的过期时间，单位为秒
        // 客户端请求唯一标识,防止误删其他客户端加的锁,可把他设置为锁的值
        $requestId = '123456'; 
        
        if (Redis::exists($lockKey)) {
            // 锁已存在，说明已经有其他进程在操作，返回错误信息
            return json_encode(['code' => 666, 'msg' => '请勿重复提交']);
        }

        try {
            // TODO 处理业务
            $res = TestRedisMysql::create($data);
            
            if($res){
                return json_encode(['code' => 200, 'msg' => '添加成功']);
            }else   {
                return json_encode(['code' => 500, 'msg' => '添加失败']);
            }
        } catch (\Exception $e) {
            // 异常处理
        } finally {
            // 处理完释放锁
            $this->unLock($lockKey, $requestId);
        }
    
            // 获取锁失败，说明已经有其他进程在操作，返回错误信息
            //return json_encode(['code' => 666, 'msg' => '请勿重复提交']);
        
        
    }
    
/*******************lua脚本实现加锁解锁原子性*************/
    //用lua加锁原子性加锁
    public function onLock($key,$val,$expire){
        $lua = <<<LUA
            local key = KEYS[1]
            local val = ARGV[1]
            local expire = ARGV[2]
            if redis.call('setnx', key, val) == 1 then
                redis.call('expire', key, expire)
                return 1
            end
            return 0
LUA;
        $res = Redis::eval($lua, 1, $key, $val, $expire);
        
        if($res == 1){
            return true;
        }else{
            return false;
        }  
    }
    //不用用lua 锁原子性加锁
    public function onLock2($key,$val,$expire){
        $res = Redis::set($key,$val,'EX',$expire,'NX');
        
        if($res == 1){
            return true;
        }else{
            return false;
        }  
    }
    //解锁原子性
    public function unLock($key,$val){
        $lua = <<<LUA
            local key = KEYS[1]
            if redis.call('get', key) == ARGV[1] then
                return redis.call('del', key)
            else
                return 0
            end
LUA;
        $res = Redis::eval($lua,1, $key, $val);
        
        
        return $res;
        
    }
/******************* ***********************************/



/*-----------------------------redis幂等性和分布式锁 end-----------------------------*/
    
    
}
